package cn.jane.rpc.netty.nameservice;

import cn.jane.rpc.api.NameService;
import cn.jane.rpc.netty.serialize.SerializeSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 本地文件实现的 {@link cn.jane.rpc.api.NameService}
 */
public class LocalFileNameService implements NameService {

    private static final Logger log = LoggerFactory.getLogger(LocalFileNameService.class);

    private static final Set<String> SUPPORTED_SCHEMES = Collections.singleton("file");

    private File file;

    @Override
    public void connect(URI nameServiceUri) {
        if (!SUPPORTED_SCHEMES.contains(nameServiceUri.getScheme())) {
            throw new RuntimeException("Unsupported scheme!");
        }
        file = new File(nameServiceUri);
    }

    @Override
    public void registerService(String serviceName, URI serviceUri) throws IOException {
        log.info("Register service: [{}], uri: [{}].", serviceName, serviceUri);
        try (RandomAccessFile raf = new RandomAccessFile(file, "rw");
             FileChannel channel = raf.getChannel()) {
            // 锁定文件，防止并发读写
            FileLock lock = channel.lock();
            try {
                // 读取并解析文件中的元数据
                int fileSize = (int) raf.length();
                Metadata metadata;
                byte[] bytes;
                if (fileSize > 0) {
                    bytes = new byte[fileSize];
                    ByteBuffer buffer = ByteBuffer.wrap(bytes);
                    while (buffer.hasRemaining()) {
                        channel.read(buffer);
                    }

                    metadata = SerializeSupport.parse(bytes);
                } else {
                    metadata = new Metadata();
                }

                // 将当前服务的 URI 加入到服务 URI 集合中
                metadata.computeIfAbsent(serviceName, k -> new HashSet<>()).add(serviceUri);
                log.info(metadata.toString());

                // 将当前元数据写入文件中
                bytes = SerializeSupport.serialize(metadata);
                channel.truncate(bytes.length);
                channel.position(0L);
                int written = channel.write(ByteBuffer.wrap(bytes));
                channel.force(true);
                if (written == 0) {
                    log.error("Writing to file failed!");
                }

                log.info("Register service: [{}], uri: [{}] successfully!", serviceName, serviceUri);
            } finally {
                lock.release();
            }
        }
    }

    @Override
    public List<URI> lookupService(String serviceName) throws IOException {
        return Collections.emptyList();
    }

    @Override
    public Set<String> supportedSchemes() {
        return SUPPORTED_SCHEMES;
    }
}
